home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Pascal Super Library
/
Pascal Super Library (CW International)(1997).bin
/
DEBUG
/
WATCHES
/
WATCHES.TXT
< prev
next >
Wrap
Text File
|
1996-06-21
|
24KB
|
748 lines
(*
See/text-search in file COMM.TXT for "WATCHES.TXT".
WATCHES TXT :watches & how to format them; values & how they
are stored in memory; TP-IDE; (guess from 6.0 onwards)
................................................................
"Beginners only" {to skip, text-search "WATCH-FORMATS"
Watches invisible ?
UNITs
Watches invisible after CTRL-F9 and a run-error
Watches = Variables only?
................................................................
WATCH-FORMATS
VALUE & HOW IT IS STORED
POINTERS
MANY VARS TO DISPLAY
BINARY FORMAT
================================================================
................................................................
"Beginners only" {to skip, text-search "WATCH-FORMATS"
Watches are what you get via eg. CTRL+F7. You put variables
in the watches-window. It watches what the variables are
looking at. To watch them carefully, their value is closely
monitored. If you want to keep a close eye on this, you can
split your monitor with ALT-W, T, and have a look at the
watches in the lower window.
(BTW, you can change a value with ALT+D, E while the program is
"running", i.e. while debugging.
)
Watches invisible ?
Well, firstly you need to enable the compiler to compile
with the necessary information for debugging, before you
running a program step by step - and watching any variables.
See the online-help on eg.: $D $L
(put cursor on $D, press CTRL-F1}
((also, there are some notes in file: Define.txt of upload
loader.zip
))
UNITs
Note that if a program uses units you can step through
their routines, only if the units are compiled with
the corresponding $... specifications.
Also: If you press F8 and the highlighted line jumps to
the "BEGIN" of the main program, you can then step through
the init-code (BEGIN...END part of units) of all such
compiled units by pressing F7.
You can however, simply put an END instead of a BEGIN END
in the interface of an unit which would "avoid" this. If
there is no "real" ini-code, well, you can still put BEGIN END
in a unit, which then perhaps serves to check which units are
loaded when (stepping via F7), etcetera.
Units as CRT, SYSTEM, etc. are not compiled for this kind of
"stepping" via F7.
Hence, you'd need to press F7 or F8 to start debugging
before watches "become visible". Also, you'd need to take
care that watches inside procedures might have the same
name as global variables. Inside a procedure P, the
watch-variable "w" represents the "w" of the procedure,
outside the procedure it refers to the global variable "w".
You can specify a variable however:
Program x;
var w:word;
procedure a;
var w:word;
begin
end;
begin
a;
end.
Here you can use: "x.w" for the global variable "w", and
"a.w" for the procedure's variable "w"
The watch "w" may refer to any of these, depending on
whether "the program" is *currently* "inside the procedure"
or not.
((
Other specifications can apply to the units CRT, DOS, etc.
eg.: "crt.lastmode". You can of course also use this to
"de-reference" procedure/functions. "built-in" routines
are de-referenced with System.*
eg.:
program x; uses crt;
{---------------------------------------------------------------}
PROCEDURE writeln (s:string);
BEGIN
system.writeln(#7,s); {#7=bell character (ASCII), beeps}
END;
{---------------------------------------------------------------}
BEGIN
ClrScr;
writeln('...NET !');
System.Writeln('...first service.');
System.Writeln('...quiet, please...');
writeln('...OUT !');
END.
))
Watches invisible after CTRL-F9 and a run-error
When your program stops with a run-error the watches of a
procedure might no longer be "visible". For test-purposes
you might instead use a global variable, which will still
be visible:
(eg included via $define... compiling)
((about $define, see file DEFINE.TXT / upload: Loader.zip))
For example:
Program x;
var w:word;
procedure a;
var b:byte;
begin
b:=4;
w:=4;
runError;
{<function to artificially cause a runError as if a "real"
error had occurred.
}
end;
begin
a;
end.
Follwing a screen-copy *after* this sample code has been run:
File Edit Search Run Compile Debug Tools Options Window Help
╔═[■]════════════════════════════ WATCHES.TXT ═══════════════════════════1═[·]═╗
║ Error 0: Runtime error. ·
║ procedure a; ▒
║ var b:byte; ▒
║ begin ■
║ b:=4; ▒
║ w:=4; ▒
║ runError; ▒
║ {function to artificially cause a runError as if a "real" ▒
║ error had occurred. ▒
║ } ▒
║ end; ▒
║ begin ▒
║ a; ▒
║ end. ·
╚═·════ 73:1 ═════·■▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒·─┘
┌────────────────────────────────── Watches ─────────────────────────────2─────┐
│ b: Unknown identifier │
│ w: 4 │
│ │
│ │
│ │
└──────────────────────────────────────────────────────────────────────────────┘
F1 Help F2 Save F3 Open Alt+F9 Compile F9 Make Alt+F10 Local menu
Note: Variable w is still "visible", when variable b is not.
Watches = Variables only?
Not necessarily. You can easily enter any "expression"
If it is wrong, the IDE will tell you - that's about all.
Invalid format-specifiers are mostly be simply ignored.
Not forgetting that if you don't have a pocket calculator
handy, you can also do calculations with the IDE. Later on,
I'll show how this "calculator" could also convert decimal
values to hexa-decimal.
Program x;
var w:word;
begin
w:=4;
end.
╔═[■]══════════════════════════════ Watches ══════
║ w: 4
║ w * 30 - 50: 70
║ 1.25 * 4 + (1.2 / 3 ): 5.4
║ Oh no, it's an: Unknown identifier
║ trunc(2.4) * trunc(2.5): 4
║ w mod 2: 0
║ w > 2.5: True
║ not true: False
...etc...type-casts...etc...
................................................................
WATCH-FORMATS
You can specify a couple of formats with which the value of a
"watch-variable" is to be displayed. And, if you want ice for
dessert, you can even combine them. That is, both combinations
of values and/or variables.
I assume this to be easier to explain by giving so called
screen-dumps as examples. You can also use this file in the
IDE and check the code yourself. If you want to run some
sample code, simply move the end-of-comment sign (the
comment sign with an asterix* ) above the sample code's
"program" start-line.
By default strings and arrays might appear quite "ugly":
program x; var s:string; a:array[1..100] of char; b:byte;
begin
s[0]:=#100; for b:=32 to 132 do s[b-31]:=char(b);
for b:=100 to 200 do a[b-99]:=char(b);
end.
┌────────────────────────────────── Watches ─────────────────────────────1─────┐
│ s: ' !"#$%&''()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefg│
│ a: ('d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','│
│ │
Both variables do not fit on the screen, and you'd need to use
CURSOR-RIGHT etc. to see "higher values".
You could try this format specifier for variables:
s[40],40
=> beginning with element #40, display 40 "elements".
╔═[■]══════════════════════════════ Watches ═════════════════════════════1═[·]═╗
║ s: ' !"#$%&''()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefg·
║ s[40],40: 'G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W'■
║ a: ('d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','▒
Sometimes, you might prefer to see values in hexa-decimal format.
You can add ",h" - but this won't work with strings/chars. Yet,
alternatively you can specify ",m" which displays "WHAT IS ACTUALLY
STORED IN MEMORY", which displays in hexa-decimal format.
┌────────────────────────────────── Watches ─────────────────────────────1─────┐
│ s: ' !"#$%&''()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefg│
│ s[40],40: 'G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W'│
│ s[40],40m: 47 48 49 4A 4B 4C 4D 4E 4F 50 51 52 53 54 55 56 57 58 59 5A 5B 5C │
"s[40],40" has "s" displayed like the array-of-char "a" was displayed.
This can be considered ugly, since one char uses 4 "digits" to get
displayed, eg: 'G',
For vanilla ice-cream, you could alternatively combine the format-
specifiers ",c" (char) with ",m":
╔═[■]══════════════════════════════ Watches ═════════════════════════════1═[·]═╗
║ s: ' !"#$%&''()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefg·
║ s[40],40: 'G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W'▒
║ s[40],40mc: 'GHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmn' ■
║ a: ('d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','▒
║ a,mc: 'defghijklmnopqrstuvwxyz{|}~ÇüéâäàåçêëèïîìÄÅÉæÆôöòûùÿÖÜ¢£¥₧ƒáíóúñѪº¿⌐·
The "mc" specifier is s.th to keep in mind whenever you want
to format as "ASCII-code chars".
File WATCHES2.txt gives an example for ASM-registers as
"watches as chars" - and for other points discussed further on
here in this file.
((
BTW, the ",c" is meant to display the #0..#31 as "ASCII-chars"
(opposed to displaying them as #0, #1, etc.) Yet, this is not
a good example to include, since it may cause havoc when printing.
An example for this & "strings"-const is in file Watches3.txt
))
Now, if you want to see decimal/Hexa-decimal equivalents,
these are some ideas:
┌────────────────────────────────── Watches ─────────────────────
│ s[40],40: 'G','H','I','J','K','L','M','N','O','P','Q','R','S','
│ s[40],40m: 47 48 49 4A 4B 4C 4D 4E 4F 50 51 52 53 54 55 56 57 5
│ s[40],40md: 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
│ 'G': 'G'
│ byte('G'): 71
│ byte('G'),h: $47
│
VALUE & HOW IT IS STORED
",m" might help to visualize how "multi-byte unities" are actually
stored in memory:
program x;
var pB:^Byte; w:word;
begin
w:=259;
pB:=@w;
end.
┌────────────────────────────────── Watches ───────────────────────
│ pB^,h: $3
│ pB^,2m: 03 01
│ w: 259
│ w,h: $103
│ w,m: 03 01
",m" by default displays memory starting from the start-address
of the variable "over the length/size" of the variable.
As you can see the 2-bytes variable "w" has the hexa-VALUE $0103.
Yet, it is STORED as $0301 - as "w,m" indicates.
Also, see below about $A+ and $A- (Text-search: "!!!")
As pB is a pointer on a single byte only, pB^ does represent just
one byte of w instead of its two bytes. Therefore the format
specifier ",2" to display the two bytes stored in memory, "pB^,2m"
Since the value of pB^ is $03, it obviously points to what "w,m"
displays as the "high-byte" ("left-most" byte). More about this
will follow later on, when giving a suggestion for "Binary format"
and all that jazz.
POINTERS
( This links to file COMM.TXT (text-search for "WATCHES.TXT")
(about pointers<->strings as parameters for child-processes)
upload: loader.zip
)
PROGRAM x;
{$D+,L+,I+,R+,S+,Q+,V+,Y+}
VAR s:string; pByte:^byte;
BEGIN
pByte:= PTR( $3344, $5566 ); {artificial value for testing only}
s[0]:=#4; {<set length to 4}
{>move the "address-pByte-is-pointing-to" to the string}
move( pByte , s[1], 4); { pByte = @pByte^ }
END.
{-----------------------------------------------------------------}
╔═[■]══════════════════════════════ Watches ══════════════════════
║ pByte: Ptr($3344,$5566)
║ pByte,m: 66 55 44 33
║ @pByte^: Ptr($3344,$5566)
║ @pByte^,p: 3344:5566
║ s: 'fUD3'
║ s,m: 04 66 55 44 33 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
║ s[1],4m: 66 55 44 33
║
As you can see, while a pointer is considered to be Ptr($3344,$5566)
that is Ptr($segment,$offset), it is differently stored in memory.
Here as: pByte,m: 66 55 44 33,
Which is reflected in the type PtrRec used in unit WinDos to
"access" a pointer:
TYPE
PtrRec = RECORD
ofs, seg:word;
END;
Now, to obtain the word-value of eg. the segment you might eg. use
the function seg():
"w:=seg(pointerVar)"
or type-casting:
"w:= PtrRec( pointerVar ).seg" (etc.... word()<-> offset, etc.)
In a Bpascal forum message, Peter Below (CIS address 100113,1101)
strongly recommended to me using type-casting, saying that, the
Seg/Ofs functions would actually load the value into CPU-registers,
and that with invalid values this would cause a GPF.
MANY VARS TO DISPLAY
If you have many byte/words as watches, each will "take up" one line.
Even if they might not need more than a couple of columns to display
a value.
To not waste all those lines, you might eg. declare them following
one another in the var-statement, eg: var a,b,c,d:byte;
program x;
var a,b:byte; c:byte; d:byte;
s:string;
e:byte;
begin
a:=1;b:=2;c:=3;d:=4;e:=5;
move(a,s[1],5);
end.
┌────────────────────────────────── Watches ─────────────────────────────
│ a: 1
│ b: 2
│ c: 3
│ d: 4
│ e: 5
│ a,5: 1,2,3,4,0
│ s,m: 00 01 02 03 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Note
-"a,5" does not display "e", as variable "s" was declared before "e".
-(BTW, the 0 in a,5 is obviously the length byte of s)
(((
....BTW...BEGIN...................
While using
var a,b:byte; c:byte;
or
var a,b,c:byte;"
did not seem to make any difference, I found a difference when
using "var" several times:
program x;
var a,b:byte;
var c:byte;
var d:byte;
var s:string;
e:byte;
begin
a:=1;b:=2;c:=3;d:=4;e:=5;
move(a,s[1],5);
end.
┌────────────────────────────────── Watches ───────────
│ a: 1
│ b: 2
│ c: 3
│ d: 4
│ e: 5
│ a,5: 1,2,3,0,4
│ s,m: 00 01 02 03 00 04 00 00 00 00 00 00 00 00 00 00
And mixing bytes/words:
program x;
{$A+,B-,D+,E+,F-,G-,I+,L+,N-,O-,P-,Q-,R-,S+,T-,V+,X+}
var a,b,c:byte; d:word; e:byte;
var s:string;
begin
a:=1;b:=2;c:=3;d:=259;e:=5;
move(a,s[1],7);
end.
┌────────────────────────────────── Watches ─────
│ a: 1
│ b: 2
│ c: 3
│ e: 5
│ a,7: 1,2,3,0,3,1,5
│ a,7m: 01 02 03 00 03 01 05
│ s,m: 00 01 02 03 00 03 01 05 00 00 00 00 00 00
│ d,m: 03 01
│ e,2m: 05 00
│
!!! And the effect $A+ and $A- can have.
program x;
{$A-,B-,D+,E+,F-,G-,I+,L+,N-,O-,P-,Q-,R-,S+,T-,V+,X+}
var a,b,c:byte; d:word; e:byte;
var s:string;
begin
a:=1;b:=2;c:=3;d:=259;e:=5;
move(a,s[1],7);
end.
┌────────────────────────────────── Watches ─────
│ a: 1
│ b: 2
│ c: 3
│ e: 5
│ a,7: 1,2,3,3,1,5,0
│ a,7m: 01 02 03 03 01 05 00
│ s,m: 00 01 02 03 03 01 05 00 00 00 00 00 00 00
│ d,m: 03 01
│ e,2m: 05 00
│
$A+ a,7: 1,2,3,0,3,1,5
$A- a,7: 1,2,3,3,1,5,0
($A+ has "inserted a 0" before the word-var "d" (here the "3,1")
- for details see online help on $A (put cursor on $A, press CTRL+F1)
or manuals
)
....BTW...END...................
)))
Or, as I was suggested with a <g>, group them into records:
program x;
const ar :record
a,b,c,d,e:byte;
end = (a:1 ; b:2 ; c:3 ; d:4 ; e:5 );
begin
end.
╔═[■]══════════════════════════════ Watches ═════════════════════════════
║ ar: (1,2,3,4,5)
║ ar,r: (a:1;b:2;c:3;d:4;e:5)
║
NOTE that the format-specifier ",r" adds the names of the
record-elements, which could be quite useful for larger records.
NOTE
Mixing CONST and VAR declaration won't be useful - the Borland's
Programmer manual indicates different "locations" for the "storage"
of CONST variables.
That is to say for example:
{$A- compiled}
const byteVar:byte=$80;
userInput:string='';
will have userInput[0] directly after byteVar -
Yet, this won't work for:
const byteVar:byte=$80;
var userInput:string;
See Borland's programmer manual on details about "internals,
how variables are stored", CONST
(again, I can't give titles or pages, since I don't have the
manuals in English
)
BINARY FORMAT
There seem to be no format-specifiers for eg. binary or octal format.
I'd suggest one of my procedures enclosed here for this conversion.
It can surely be improved (eg. wouldn't work on x.xxxx, i.e. digits
after the "decimal"-point). Possible use while testing could be
adding test-code via $defined-compiling and then using a watch-variable
which displays the binary value.
-------------------------------------------------------------------------
basisDesZahlenSystemsFuerDenString:
= the base of the system, eg 2 for binary, 8 for octal, 10 for decimal.
dezimalzahl2string converts to "any system", just that you'd need to
expand "ch" for anything with a higher base as 16.
(((
BTW:
Actually the decimal-system is the odd one out. Converting from
binary to octal to hexadecimal is much easier. "laengeGruppierung"
gives the number of "chars" needed for conversion:
8 binary chars = 3 octal chars = 2 hexa-decimal chars.
eg: binary 11111111
= 11 111 111
octal 3 7 7
= 1111 1111
hexa F F
If you'd like some homework, convert your Compuserve-Address from
its octal format to hexa-decimal format.
Or convert the hexa-decimal value $000000000009 to decimal format.
-------------------------------------------------------------------------
)))
*)
program x;
var pB:^Byte; w:word; s:string;
{-DDDD---------------------------------------------------------------------}
FUNCTION dezimalzahl2string
( zahl:longint;
basisDesZahlenSystemsFuerDenString:byte
) :string;
CONST ch:array[0..15]of Char = '0123456789ABCDEF'; {max. für hexa-zahlen}
VAR s:string; laengeGruppierung:byte;
BEGIN
IF zahl < 0
THEN s:='-'
ELSE s[0]:=#0;
REPEAT
{Beim Restwertverfahren ergeben sich die Vorkommastellen "rückwärts"}
s:=ch[ (zahl mod basisDesZahlenSystemsFuerDenString) ] + s;
zahl:= zahl div basisDesZahlenSystemsFuerDenString;
UNTIL zahl = 0;
CASE basisDesZahlenSystemsFuerDenString OF
2:laengeGruppierung:=8;
8:laengeGruppierung:=3;
16:laengeGruppierung:=2;
ELSE laengeGruppierung:=0;
END;
IF laengeGruppierung<>0
THEN while (byte(s[0]) mod laengeGruppierung) <> 0 do s:='0'+s
;
dezimalzahl2string:=s;
END; {dezimalzahl2string}
{--------------------------------------------------------------------------}
FUNCTION int2binStr (CONST zahl:longint):string;
VAR s:string; b:byte;
BEGIN
s:=dezimalZahl2String(zahl, 2);
b:=byte(s[0]);
while b>0 do begin
insert(' ',s,b-3);
insert(' ',s,b-7);
dec(b,8);
end;
int2BinStr:='BIN:'+s;
END; {Wandelt integer in eine Binärzahl im Format eines Strings}
{--------------------------------------------------------------------------}
begin
w:=259;
s:='w->'+int2BinStr(w);
pB:=@w;
s:=s+' // pb->'+int2BinStr(pB^);
{ At this stage of the program, the watches would display:
┌────────────────────────────────── Watches ───────────────────────
│ pB^,h: $3
│ pB^,2m: 03 01
│ w: 259
│ w,h: $103
│ w,m: 03 01
│ s[1],30mc: 'w->BIN: 0000 0001 0000 0011 '
│ s[30],60mc: ' // pb->BIN: 0000 0011
│
}
inc(pB);
s:=s+' // inc(pB)->'+int2BinStr(pB^);
{ At this stage of the program, the watches would display:
┌────────────────────────────────── Watches ───────────────────────
│ pB^,h: $1
│ pB^,2m: 01 50
│ w: 259
│ w,h: $103
│ w,m: 03 01
│ s[1],30mc: 'w->BIN: 0000 0001 0000 0011 '
│ s[30],60mc: ' // pb->BIN: 0000 0011 // inc(pB)->BIN: 0000 0001
│
}
END.
If you look at "w" as its value:
'w->BIN: 0000 0001 0000 0011 '
inc(pB) seems to move "from the right to the left":
pb->BIN: 0000 0011
inc(pB)->BIN: 0000 0001
This is not the case. As mentioned you need to look at "w",
as it is stored in memory, which is what "w,m" displays.
Hence pB did move from "the left to the right" as to be expected.
(Copyright)Armin Schmitt at 100552.1041@compuserve.com
Nov-1995,
jC,v960224
Homework:
octal 100552.1041
= 1 0 0 5 5 2 . 1 0 4 1
=bin. 001 000 000 101 101 010 . 001 000 100 001
= 001000000101101010 . 001000100001
= 00 1000 0001 0110 1010 . 0010 0010 0001
=hexa.dec. 8 1 6 A . 2 2 1
=
hexadecimal 816A.221
hexadecimal $000000000009
=
decimal 9.0000000000000